package controller

import (
	"encoding/json"
	"fmt"
	"go-toy/util"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
)

// Query params struct for list request
type QueryParamsStruct struct {
	KeyValue   any    `json:"key_value"`
	KeyType    string `json:"key_type"` // options for list request: value, type, default, options, agent
	KeyDefault any    `json:"key_default"`
	KeyOptions []any  `json:"key_options"`
	KeyAgent   string `json:"key_agent"`
}

type Common struct {
	PageNum     int
	PageLimit   int
	PageOffset  int
	OrderBy     []string
	Conditions  map[string]any
	QueryParams map[string]QueryParamsStruct
}

/**
 * Handle params which used for pagination
 * Example: page=1&limit=10
 */
func (c *Common) HandlePage(g *gin.Context) (pageNum int, pageLimit int, pageOffset int) {
	page := g.Query("page")
	limit := g.Query("limit")

	if page == "" {
		pageNum = 1
	} else {
		pageNum, _ = strconv.Atoi(page)
	}

	if limit == "" {
		pageLimit = 10
	} else {
		pageLimit, _ = strconv.Atoi(limit)
	}
	if pageLimit > 100 {
		pageLimit = 100
	}
	pageOffset = (pageNum - 1) * pageLimit

	c.PageNum = pageNum
	c.PageLimit = pageLimit
	c.PageOffset = pageOffset

	return pageNum, pageLimit, pageOffset
}

/**
 * Handle params which used for sort
 * Example: sort=+id,-name
 * +id means sort by id in ascending order
 * -name means sort by name in descending order
 */
func (c *Common) HandleSort(gc *gin.Context) []string {
	sort := gc.Query("sort")
	var orderBy []string

	if sort != "" {
		fields := strings.Split(sort, ",")
		for _, field := range fields {
			if len(field) > 1 {
				direction := field[0]
				column := field[1:]

				switch direction {
				case '+':
				case ' ':
					orderBy = append(orderBy, fmt.Sprintf("%s ASC", column))
				case '-':
					orderBy = append(orderBy, fmt.Sprintf("%s DESC", column))
				default:
					orderBy = append(orderBy, fmt.Sprintf("%s ASC", field))
				}
			}
		}
	}
	c.OrderBy = orderBy

	return orderBy
}

/**
 * Handle query params which for filter data
 */
func (c *Common) HandleCondition(gc *gin.Context) {
	conditions := make(map[string]any)

	var hasKeyValue bool
	var result any
	for key, value := range c.QueryParams {
		if value.KeyValue, hasKeyValue = gc.GetQuery(key); hasKeyValue {
			if !util.IsString(value.KeyValue) {
				value.KeyValue = value.KeyValue.(string)
			}
			switch value.KeyType {
			case "string":
				value.KeyValue = strings.TrimSpace(value.KeyValue.(string))
			case "int":
				value.KeyValue, _ = strconv.Atoi(value.KeyValue.(string))
			case "float":
				value.KeyValue, _ = strconv.ParseFloat(value.KeyValue.(string), 64)
			case "number":
				value.KeyValue, _ = strconv.ParseFloat(value.KeyValue.(string), 64)
			case "many":
				value.KeyValue = strings.Split(value.KeyValue.(string), ",")
			case "array":
				err := json.Unmarshal([]byte(value.KeyValue.(string)), &result)
				if err != nil {
					value.KeyValue = []any{}
				}
			case "datestart":
				result = util.DatetimeToUnix(value.KeyValue.(string), "date")
				value.KeyValue = result.(int)
			case "dateend":
				result = util.DatetimeToUnix(value.KeyValue.(string), "date")
				value.KeyValue = result.(int) + 86400 - 1
			case "date":
				result = util.DatetimeToUnix(value.KeyValue.(string), "date")
				value.KeyValue = result.(int)
			case "datetime":
				result = util.DatetimeToUnix(value.KeyValue.(string), "datetime")
				value.KeyValue = result.(int)
			}

			if value.KeyValue != "" {
				conditions[key] = value.KeyValue
			} else {
				if value.KeyDefault != "" {
					// conditions[key] = value.KeyDefault.(string)
				}
			}
		}
	}

	c.Conditions = conditions
}

/**
 * Handle json params for create/update request
 */
func (c *Common) HandleJsonParams(gc *gin.Context, structPtr *interface{}) {
	method := gc.Request.Method
	if method == "POST" || method == "PUT" {
		gc.ShouldBindJSON(structPtr)
	}
}

/**
 * Initialize common params
 */
func (c *Common) Initialize(gc *gin.Context) {
	c.HandlePage(gc)
	c.HandleSort(gc)
	c.HandleCondition(gc)
}

func (c *Common) SuccessListResult(gc *gin.Context, result interface{}, total int64) {
	util.Response.ResponseSuccess(gc, gin.H{
		"list":  result,
		"total": total,
		"page":  c.PageNum,
		"limit": c.PageLimit,
	})
}
